Practical Data Science: Analyzing Stock Market Data with R

Counter-Trend Systems

In this lecture we’ll look at: * Momentum Indicators * Volatility Indicator * Counter-Trend Systems

Counter-trend systems are tricky. You trade raw counter trends when you’re sure you’re in a range-bound market and are trading at the extremes otherwise you use added indicators to stay aligned with longer-term trends. Raw counter-trend trading feels like picking tops and bottoms, and those rarely work out. Here we’ll focus on trading the short-term counter trend, while following the long-term trend.

Let’s get some data:

library(binhf)
library(quantmod)
getSymbols(c('EWP', 'SPY'), src='google')

# remove any NAs 
EWP <- EWP[!(rowSums(is.na(EWP))),]
SPY <- SPY[!(rowSums(is.na(SPY))),]

Momentum Indicators

We’re going to look at 3 interesting momentum indicators that capture short-term cycles:

Relative Strength Index (RSI), is an momentum indicator that measures movement. Its author, J. Welles Wilder, recommends using a period of 14 and when it is over 70, it is strongly bought (or overbought) and under 30, it is strongly sold (or oversold).

Commodity Channel Index (CCI) by Donald Lambert, is a price-derived indicator revolving around 0, where 100 is usually considered overbought and -100, oversold.

Rate of Change (ROC), also a momentum indicator, looks at accelerating and decelerating market moves.

Let’s look at all 3 of them with a 20-period setting:

chartSeries(EWP, theme="white", TA="addRSI(n=100);addCCI(n=100);addROC(n=100)", subset='2015')

chartSeries(SPY, theme="white", TA="addRSI(n=100);addCCI(n=100);addROC(n=100)")

Counter-Trend Systems

For our counter-trend system, we will counter a faster cycle but stay in the direction of the slower one. In essence, we’re trading with the slow trend but against the fast one. While in the previous systems, we only took a trade while both directions aligned in the direction of the long-term trend.

The key is to use one of the derived indicators that best signals overbought/oversold signals.

We’ll try each one of them with a long-term EMA.

CCI:

chartSeries(EWP, theme="white", TA="addCCI(n=100);addEMA(n=50,col='blue');addEMA(n=200,col='red')")

# create a slow ema difference
EWP.EMA.50 <- EMA(EWP$EWP.Close, n=50) 
EWP.EMA.200 <- EMA(EWP$EWP.Close, n=200) 
Slow.Diff <- EWP.EMA.50 - EWP.EMA.200
CCI.IND <- CCI(HLC=EWP[,c("EWP.High","EWP.Low","EWP.Close")],n=100)

# look for long entries
Long_Trades <- ifelse(
     shift(v=as.numeric(CCI.IND), places=1, dir="right") > CCI.IND &
        CCI.IND < 100 & 
        Slow.Diff > 0, EWP$EWP.Close, NA)

# look for short entries
Short_Trades <- ifelse(
       shift(v=as.numeric(CCI.IND), places=1, dir="right") < CCI.IND &
        CCI.IND > -100 & 
         Slow.Diff < 0, EWP$EWP.Close, NA)

plot(EWP)
## Warning in plot.xts(EWP): only the univariate series will be plotted
points(Long_Trades, col='blue', cex=1.5, pch=18)
points(Short_Trades, col='red', cex=1.5, pch=18)

Volatility indicator

Chaikin Volatility, uses the high, low, close for its accumulation/distribution and subtracts two moving averages of different periods of the AD.

Chaikin Volatility:

chartSeries(EWP, theme="white", TA="addChVol(n=100);")

chartSeries(EWP, theme="white", TA="addCCI(n=100);addEMA(n=50,col='blue');addEMA(n=200,col='red');addChVol(n=100);")

# create a slow ema difference
EWP.EMA.50 <- EMA(EWP$EWP.Close, n=50) 
EWP.EMA.200 <- EMA(EWP$EWP.Close, n=200) 
Slow.Diff <- EWP.EMA.50 - EWP.EMA.200
CCI.IND <- CCI(HLC=EWP[,c("EWP.High","EWP.Low","EWP.Close")],n=100)
CV.IND <- chaikinVolatility(HL=EWP[,c("EWP.High","EWP.Low")], n=100)

# look for long entries
Long_Trades <- ifelse(
     shift(v=as.numeric(CCI.IND), places=1, dir="right") > CCI.IND &
        CCI.IND < 100 & 
        CV.IND < 0 & 
        Slow.Diff > 0, EWP$EWP.Close, NA)

# look for short entries
Short_Trades <- ifelse(
       shift(v=as.numeric(CCI.IND), places=1, dir="right") < CCI.IND &
        CCI.IND > -100 & 
        CV.IND < 0 & 
        Slow.Diff < 0, EWP$EWP.Close, NA)

plot(EWP)
## Warning in plot.xts(EWP): only the univariate series will be plotted
points(Long_Trades, col='blue', cex=1.5, pch=18)
points(Short_Trades, col='red', cex=1.5, pch=18)

What about shifting further back on the CCI, this ensures that it is a retracement and not a random bump…

RSI:

chartSeries(EWP, theme="white", TA="addRSI(n=100);", subset='2015')

chartSeries(EWP, theme="white", TA=NULL, subset='2015')

RSI.Fast <- RSI(price=EWP$EWP.Close,n=10)
RSI.Slow <- RSI(price=EWP$EWP.Close,n=30)
RSI.Diff <- RSI.Fast-RSI.Slow
addTA(RSI.Diff, col='blue', type='h',legend="RSI Diff")

# create a slow ema difference
EWP.EMA.50 <- EMA(EWP$EWP.Close, n=50) 
EWP.EMA.200 <- EMA(EWP$EWP.Close, n=200) 
Slow.Diff <- EWP.EMA.50 - EWP.EMA.200

RSI.IND <- RSI(price=EWP$EWP.Close,n=30)

# look for long entries
Long_Trades <- ifelse(
        RSI.Diff  < 0 &
        shift(v=as.numeric(RSI.Diff ), places=1, dir="right") > 0  &
        Slow.Diff > 0, EWP$EWP.Close, NA)

# look for short entries
Short_Trades <- ifelse(
        RSI.Diff  > 0 &
        shift(v=as.numeric(RSI.Diff ), places=1, dir="right") < 0  &
        Slow.Diff < 0, EWP$EWP.Close, NA)

plot(EWP, main='RSI')
## Warning in plot.xts(EWP, main = "RSI"): only the univariate series will be
## plotted
points(Long_Trades, col='blue', cex=1, pch=18)
points(Short_Trades, col='red', cex=1, pch=18)

Lets see if we can improve this by adding the Chaikin Volatility to the RSI like we did earlier with the CCI counter-trading system.

chartSeries(EWP, theme="white", TA="addRSI(n=100);addChVol(n=100);", subset='2015')

chartSeries(EWP, theme="white", TA=NULL, subset='2015')

RSI.Fast <- RSI(price=EWP$EWP.Close,n=10)
RSI.Slow <- RSI(price=EWP$EWP.Close,n=30)
RSI.Diff <- RSI.Fast-RSI.Slow
addTA(RSI.Diff, col='blue', type='h',legend="RSI Diff")

# create a slow ema difference
EWP.EMA.50 <- EMA(EWP$EWP.Close, n=50) 
EWP.EMA.200 <- EMA(EWP$EWP.Close, n=200) 
Slow.Diff <- EWP.EMA.50 - EWP.EMA.200
CV.IND <- chaikinVolatility(HL=EWP, n=100)
RSI.IND <- RSI(price=EWP$EWP.Close,n=30)

# look for long entries
Long_Trades <- ifelse(
        RSI.Diff  < 0 &
        shift(v=as.numeric(RSI.Diff ), places=1, dir="right") > 0  &
        CV.IND < -0.1 &
        Slow.Diff > 0, EWP$EWP.Close, NA)

# look for short entries
Short_Trades <- ifelse(
        RSI.Diff  > 0 &
        shift(v=as.numeric(RSI.Diff ), places=1, dir="right") < 0  &
        CV.IND < -0.1 &
        Slow.Diff < 0, EWP$EWP.Close, NA)

plot(EWP, main='RSI')
## Warning in plot.xts(EWP, main = "RSI"): only the univariate series will be
## plotted
points(Long_Trades, col='blue', cex=1, pch=18)
points(Short_Trades, col='red', cex=1, pch=18)

Let’s add a second RSI shifted period that captures more counter-trend hesitancy:

# create a slow ema difference
EWP.EMA.50 <- EMA(EWP$EWP.Close, n=50) 
EWP.EMA.200 <- EMA(EWP$EWP.Close, n=200) 
Slow.Diff <- EWP.EMA.50 - EWP.EMA.200

RSI.IND <- RSI(price=EWP$EWP.Close,n=30)

# look for long entries
Long_Trades <- ifelse(
        RSI.Diff  < 0 &
        shift(v=as.numeric(RSI.Diff ), places=1, dir="right") > 0  &
        shift(v=as.numeric(RSI.Diff ), places=2, dir="right") < 0  &
        Slow.Diff > 0, EWP$EWP.Close, NA)

# look for short entries
Short_Trades <- ifelse(
        RSI.Diff  > 0 &
        shift(v=as.numeric(RSI.Diff ), places=1, dir="right") < 0  &
        shift(v=as.numeric(RSI.Diff ), places=2, dir="right") > 0  &
        Slow.Diff < 0, EWP$EWP.Close, NA)

plot(EWP, main='RSI')
## Warning in plot.xts(EWP, main = "RSI"): only the univariate series will be
## plotted
points(Long_Trades, col='blue', cex=1, pch=18)
points(Short_Trades, col='red', cex=1, pch=18)

Let’s try this final system on the S&P 500

chartSeries(SPY, theme="white", TA="addRSI(n=100);addChVol(n=100);")

# create a slow ema difference
SPY.EMA.50 <- EMA(SPY$SPY.Close, n=50) 
SPY.EMA.200 <- EMA(SPY$SPY.Close, n=200) 
Slow.Diff <- SPY.EMA.50 - SPY.EMA.200

RSI.Fast <- RSI(price=SPY$SPY.Close,n=10)
RSI.Slow <- RSI(price=SPY$SPY.Close,n=30)
RSI.Diff <- RSI.Fast-RSI.Slow

CV.IND <- chaikinVolatility(HL=SPY, n=100)

# look for long entries
Long_Trades <- ifelse(
        CV.IND < -0.1 &
        RSI.Diff  < 0 &
        shift(v=as.numeric(RSI.Diff ), places=1, dir="right") > 0  &
        shift(v=as.numeric(RSI.Diff ), places=2, dir="right") < 0  &
        Slow.Diff > 0, SPY$SPY.Close, NA)

# look for short entries
Short_Trades <- ifelse(
        CV.IND < -0.1 &
        RSI.Diff  > 0 &
        shift(v=as.numeric(RSI.Diff ), places=1, dir="right") < 0  &
        shift(v=as.numeric(RSI.Diff ), places=2, dir="right") > 0  &
        Slow.Diff < 0, SPY$SPY.Close, NA)

plot(SPY, main='RSI')
## Warning in plot.xts(SPY, main = "RSI"): only the univariate series will be
## plotted
points(Long_Trades, col='blue', cex=1, pch=18)
points(Short_Trades, col='red', cex=1, pch=18)